﻿using LightCAD.MathLib;
namespace QdArch
{
    public class QdWindowAttribute : CptAttribute
    {
        public QdWindowAttribute(string category, string subCategorys) : base(category, subCategorys)
        {

        }

        public const string BuiltinUuid = "{4D611A24-3166-4EB9-919C-DBB3048ED043}";
        public override LcComponentDefinition GetBuiltinCpt(string subCategory)
        {
            return new QdWindowDef(BuiltinUuid, "内置", "窗");
        }
    }

    [QdWindowAttribute("窗", "普通窗")]
    public class QdWindowDef : LcComponentDefinition
    {
        static QdWindowDef()
        {
            CptTypeParamsDef<QdWindowDef>.ParamsDef = new ParameterSetDefinition {
                new ParameterDefinition { DataType = LcDataType.String, Name = "MaterialId", DisplayName = "材质", },
            };
        }
        public QdWindowDef()
        {
            this.TypeParameters = new LcParameterSet(CptTypeParamsDef<QdWindowDef>.ParamsDef);
        }
        public QdWindowDef(string uuid, string name, string subCategory)
            : base(uuid, name, "窗", "普通窗", new LcParameterSet(CptTypeParamsDef<QdWindowDef>.ParamsDef))
        {
            this.Parameters = new ParameterSetDefinition()
            {
                new ParameterDefinition { DataType = LcDataType.Double, Name = "Thickness", DisplayName = "厚度", },
                new ParameterDefinition { DataType = LcDataType.Double, Name = "Width", DisplayName = "宽度", },
                new ParameterDefinition { DataType = LcDataType.Double, Name = "Height", DisplayName = "高度", },
                new ParameterDefinition { DataType = LcDataType.Double, Name = "Bottom", DisplayName= "底高度" } ,
                new ParameterDefinition { DataType = LcDataType.Bool, Name = "IsLeft", DisplayName = "是否左开", },
                new ParameterDefinition { DataType = LcDataType.Bool, Name = "IsNear", DisplayName = "是否在墙体左侧", },
                new ParameterDefinition { DataType = LcDataType.Double, Name = "Angle", DisplayName = "角度(0-90)", },
                new ParameterDefinition { DataType = LcDataType.Bool, Name = "IsSingle", DisplayName = "单扇/双扇", },
            };

            this.Curve2dProvider = new Curve2dProvider("普通窗")
            {
                GetCurve2dsFunc = (p) =>
                {
                    ListEx<Curve2d> curves = null;

                    //var isSingle = p.GetValue<bool>("Size");
                    if (false)
                    {
                        curves = GetShape_单线窗(p);
                    }
                    else
                    {
                        curves = GetShape_三线窗(p);
                    }
                    return new Curve2dGroupCollection() { new Curve2dGroup()
                        {
                            Curve2ds=curves
                        }};
                }
            };

            this.Solid3dProvider = new Solid3dProvider("平开门")
            {
                GetSolid3dsFunc = (p) =>
                {
                    var solid3d = new Solid3d();
                    return GetShape3D(p);
                }
            };
        }



        public ListEx<Curve2d> GetShape_单线窗(LcParameterSet pset)
        {

            var sc = new Shape2dCreator();

            var w = (double)pset["Width"];
            //var th = (double)pset["Thickness"];
            var isLeft = (bool)pset["IsLeft"];
            var isNear = (bool)pset["IsNear"];

            var pL = sc.Point(-w / 2, 0);
            var pR = sc.Point(w / 2, 0);
            sc.DrawLine(pL.Clone(), pR.Clone());
            //var pLNear = sc.RotatePoint(pL, pR, 45);
            //var pLFar = sc.RotatePoint(pL, pR, -45);
            //var pRNear = sc.RotatePoint(pR, pL, -45);
            //var pRFar = sc.RotatePoint(pR, pL, 45);
            //if (isLeft && isNear)
            //{
            //    sc.DrawArc(pL, w, -45, 0);
            //    sc.DrawLine(pL, pRNear);
            //}
            //else if (isLeft && !isNear)
            //{
            //    sc.DrawArc(pL, w, 0, +45);
            //    sc.DrawLine(pL, pRFar);
            //}
            //else if (!isLeft && isNear)
            //{
            //    sc.DrawArc(pR, w, 180, 180 + 45);
            //    sc.DrawLine(pR, pLNear);
            //}
            //else if (!isLeft && !isNear)
            //{
            //    sc.DrawArc(pR, w, 180 - 45, 180);
            //    sc.DrawLine(pR, pLFar);
            //}

            return sc.Curves;
        }

        public ListEx<Curve2d> GetShape_三线窗(LcParameterSet pset)
        {

            var sc = new Shape2dCreator();

            var w = (double)pset["Width"];
            var t = (double)pset["Thickness"];
            //var th = (double)pset["Thickness"];
            var isLeft = (bool)pset["IsLeft"];
            var isNear = (bool)pset["IsNear"];

            // pTL_______pTR
            //|             |
            // pCL ———pcR
            //|             |
            // pBL________pBR

            var pTL = sc.Point(-w / 2, t / 2);
            var pTR = sc.Point(w / 2, t / 2);
            var pCL = sc.Point(-w / 2, 0);
            var pCR = sc.Point(w / 2, 0);
            var pBL = sc.Point(-w / 2, -t / 2);
            var pBR = sc.Point(w / 2, -t / 2);

            sc.DrawLine(pTL.Clone(), pTR.Clone());
            sc.DrawLine(pCL.Clone(), pCR.Clone());
            sc.DrawLine(pBL.Clone(), pBR.Clone());
            sc.DrawLine(pTL.Clone(), pBL.Clone());
            sc.DrawLine(pTR.Clone(), pBR.Clone());
            //var pLNear = sc.RotatePoint(pL, pR, 45);
            //var pLFar = sc.RotatePoint(pL, pR, -45);
            //var pRNear = sc.RotatePoint(pR, pL, -45);
            //var pRFar = sc.RotatePoint(pR, pL, 45);
            //if (isLeft && isNear)
            //{
            //    sc.DrawArc(pL, w, -45, 0);
            //    sc.DrawLine(pL, pRNear);
            //}
            //else if (isLeft && !isNear)
            //{
            //    sc.DrawArc(pL, w, 0, +45);
            //    sc.DrawLine(pL, pRFar);
            //}
            //else if (!isLeft && isNear)
            //{
            //    sc.DrawArc(pR, w, 180, 180 + 45);
            //    sc.DrawLine(pR, pLNear);
            //}
            //else if (!isLeft && !isNear)
            //{
            //    sc.DrawArc(pR, w, 180 - 45, 180);
            //    sc.DrawLine(pR, pLFar);
            //}

            return sc.Curves;
        }


        private Solid3dCollection GetShape3D(LcParameterSet pset)
        {
            var thickness = (double)pset["Thickness"];
            var glassThickness = 10;
            var frameWidth = 50;
            var bottom = (double)pset["Bottom"];
            var height = (double)pset["Height"];
            var width = (double)pset["Width"];
            var start = new Vector3(-width / 2, 0, bottom);
            var end = new Vector3(width / 2, 0, bottom);
            var lineVec = new Vector3().SubVectors(end, start);
            var len = lineVec.Length();
            var xAxis = lineVec.Clone().Normalize();
            var yAxis = VectorUtil.ZAxis.Clone();
            var zAxis = new Vector3().CrossVectors(xAxis, yAxis).Normalize();
            var coodMat = new Matrix4();
            coodMat.MakeBasis(xAxis, yAxis, zAxis);
            coodMat.SetPosition(start);
            var glassShape = ShapeUtil.Rect(frameWidth, frameWidth, len - frameWidth, height - frameWidth);
            var frameShape = ShapeUtil.Rect(0, 0, len, height);
            frameShape.holes.Add(glassShape);
            var glassGeoData = GeoUtil.GetStretchGeometryData(glassShape, coodMat, glassThickness / 2, -glassThickness / 2);
            var frameGeoData = GeoUtil.GetStretchGeometryData(frameShape, coodMat, thickness / 2, -thickness / 2);

            var solids = new Solid3dCollection()
            {
                new Solid3d()
                {
                    Geometry = new LightCAD.MathLib.GeometryData()
                    {
                        Verteics = glassGeoData.Position.ToArray(),
                        Indics = glassGeoData.Index.ToArray(),
                        Groups = new GeometryGroup[1]
                        {
                            new GeometryGroup{ Name = "Geometry", Start = 0, Count = glassGeoData.Index.Length, MaterialIndex = 0 },
                        }
                    }
                },
                new Solid3d()
                {
                    Geometry = new LightCAD.MathLib.GeometryData()
                    {
                        Verteics = frameGeoData.Position.ToArray(),
                        Indics = frameGeoData.Index.ToArray(),
                        Groups = new GeometryGroup[1]
                        {
                            new GeometryGroup{ Name = "Geometry", Start = 0, Count = frameGeoData.Index.Length, MaterialIndex = 0 },
                        }
                    }
                }
            };
            return solids;
        }
    }

    public class QdWindowInstance : LcComponentInstance
    {
        public QdWindowDef WindowDef { get => this.Definition as QdWindowDef; set => this.Definition = value; }
        /// <summary>
        /// 在墙上用中心插入
        /// </summary>
        public QdWall Wall { get; set; }

        public Vector2 StartPoint { get; set; }
        public Vector2 EndPoint { get; set; }
        public Vector2 ControlPoint { get; set; }

        public double Thickness
        {
            get
            {
                return Convert.ToDouble(this.Parameters["Thickness"]);
            }
            set
            {
                this.Parameters["Thickness"] = value;
            }
        }
        public double Width
        {
            get
            {
                return Convert.ToDouble(this.Parameters["Width"]);
            }
            set
            {
                this.Parameters["Width"] = value;
            }
        }
        public double Height
        {
            get
            {
                return Convert.ToDouble(this.Parameters["Height"]);
            }
            set
            {
                this.Parameters["Height"] = value;
            }
        }
        public double Bottom
        {
            get
            {
                return Convert.ToDouble(this.Parameters["Bottom"]);
            }
            set
            {
                this.Parameters["Bottom"] = value;
            }
        }
        public bool IsLeft
        {
            get
            {
                return Convert.ToBoolean(this.Parameters["IsLeft"]);
            }
            set
            {
                this.Parameters["IsLeft"] = value;
            }
        }
        public bool IsNear
        {
            get
            {
                return Convert.ToBoolean(this.Parameters["IsNear"]);
            }
            set
            {
                this.Parameters["IsNear"] = value;
            }
        }

        public QdWindowInstance(QdWindowDef windowRef) : base(windowRef)
        {

        }
        public QdWindowInstance(double thickness, double width, double height, double bottom, bool isLeft, bool isNear, double angle, bool isSingle, QdWindowDef windowDef) : base(windowDef)
        {
            this.Type = ArchElementType.Window;
            this.Parameters.SetValue("Thickness", thickness);
            this.Parameters.SetValue("Width", width);
            this.Parameters.SetValue("Height", height);
            this.Parameters.SetValue("Bottom", bottom);
            this.Parameters.SetValue("IsLeft", isLeft);
            this.Parameters.SetValue("IsNear", isNear);
            this.Parameters.SetValue("Angle", angle);
            this.Parameters.SetValue("IsSingle", isSingle);
        }
        public override Box2 GetBoundingBox()
        {
            if (this.Parameters == null || this.Parameters.Values.Length == 0)
            {
                return Box2.Empty;
            }

            return new Box2(this.Position.ToVector2(), Width, Height);
        }

        public override bool IntersectWithBox(Polygon2d testPoly, List<RefChildElement> intersectChildren = null)
        {
            Box2 thisBox = this.BoundingBox;
            return thisBox.IntersectsBox(testPoly.BoundingBox);
        }

        public override bool IncludedByBox(Polygon2d testPoly, List<RefChildElement> includedChildren = null)
        {
            Box2 thisBox = this.BoundingBox;
            return thisBox.IntersectsBox(testPoly.BoundingBox);
        }

        public override void OnRemoveAfter()
        {
            this.Wall.RemoveLcComponent(this, "Window", AssociateType.Cross);
        }
        public override LcElement Clone()
        {
            var clone = Document.CreateObject<QdWindowInstance>();
            clone.Copy(this);
            clone.Initilize(this.Document);
            return clone;
        }

        public override void Copy(LcElement src)
        {
            base.Copy(src);
            var winRef = (QdWindowInstance)src;
            this.Parameters = winRef.Parameters;
            this.WindowDef = winRef.WindowDef;
            this.Wall = winRef.Wall;
            this.StartPoint = winRef.StartPoint;
            this.EndPoint = winRef.EndPoint;
            this.ControlPoint = winRef.ControlPoint;
            this.Width = winRef.Width;
            this.Height = winRef.Height;
            this.Bottom = winRef.Bottom;
            this.IsLeft = winRef.IsLeft;
            this.IsNear = winRef.IsNear;

        }

        public override void ReadObjectRefs<T>(ICollection<T> objs)
        {
            if (this.Wall != null)
            {
                var wallId = this.Wall.Id;
                this.Wall = objs.FirstOrDefault(o => o.Id == wallId) as QdWall;
            }
            if (this.Wall != null && this.WindowDef != null)
            {
                this.Wall.AddLcComponent(this, "Window", AssociateType.Cross);
            }
        }
        public override void WriteProperties(Utf8JsonWriter writer, JsonSerializerOptions soptions)
        {
            base.WriteProperties(writer, soptions);

            writer.WriteStringProperty("WindowId", this.WindowDef.Uuid);
            writer.WriteNumberProperty("WallId", this.Wall.Id);
            writer.WriteVector2dProperty(nameof(this.StartPoint), this.StartPoint);
            writer.WriteVector2dProperty(nameof(this.EndPoint), this.EndPoint);
            writer.WriteVector2dProperty(nameof(this.ControlPoint), this.ControlPoint);

            if (this.Thickness != 0)
            {
                writer.WriteNumberProperty(nameof(Thickness), this.Thickness);
            }
            if (this.Width != 0)
            {
                writer.WriteNumberProperty(nameof(Width), this.Width);
            }
            if (this.Height != 0)
            {
                writer.WriteNumberProperty(nameof(Height), this.Height);
            }
            if (this.Bottom != 0)
            {
                writer.WriteNumberProperty(nameof(Bottom), this.Bottom);
            }

            if (this.IsLeft)
            {
                writer.WriteBoolProperty(nameof(IsLeft), this.IsLeft);
            }

            if (this.IsNear)
            {
                writer.WriteBoolProperty(nameof(IsNear), this.IsNear);
            }

        }

        public override void ReadProperties(ref JsonElement jele)
        {
            //base.ReadProperties(ref jele);

            //var windowId = jele.ReadStringProperty("WindowId");
            //var cpt = this.Document.Components[windowId];
            //var window = new QdWindow(cpt.SubCategory, cpt.Name);
            //window.Uuid = windowId;
            //this.Window = window;
            //this.Document.Components.Remove(windowId);
            //this.Document.Components.Add(window);
            //this.Wall = new QdWall()
            //{
            //    Id = jele.ReadIdProperty("WallId")
            //};
            //this.Parameters = QdWindow.ParamsDefinition.New();
            //this.StartPoint = jele.ReadVector2dProperty(nameof(this.StartPoint));
            //this.EndPoint = jele.ReadVector2dProperty(nameof(this.EndPoint));
            //this.ControlPoint = jele.ReadVector2dProperty(nameof(this.ControlPoint));
            //this.Thickness = jele.ReadDoubleProperty(nameof(Thickness));
            //this.Width = jele.ReadDoubleProperty(nameof(Width));
            //this.Height = jele.ReadDoubleProperty(nameof(Height));
            //this.Bottom = jele.ReadDoubleProperty(nameof(Bottom));
            //this.IsLeft = jele.ReadBoolProperty(nameof(IsLeft)) ?? this.IsLeft;
            //this.IsNear = jele.ReadBoolProperty(nameof(IsNear)) ?? this.IsNear;
        }
    }
}